package com.functest.framework


import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.StringDescription


/**
 * @author: DavydovMD
 * Date: 01.03.13
 * Time: 14:24
 */
class Assertions {
    static private ThreadLocal<List<FailedAssert>> failedAsserts

    private static Closure logPassedAssertion = { String message ->
        BaseFunctionalTest.logger.debug(message)
    }

    static {
        failedAsserts = new InheritableThreadLocal()
    }

    public static List<FailedAssert> getFailedWeakAsserts() {
        return failedAsserts?.get()?.asImmutable()
    }

    private static boolean _weakAssertThat(String assertionDescription, Object actual, Matcher matcher, boolean silence) {
        Description description = new StringDescription()
        if (!matcher.matches(actual)) {
            description.appendText(assertionDescription)
                    .appendText("\nExpected: ")
                    .appendDescriptionOf(matcher)
                    .appendText("\n     but: ")
            matcher.describeMismatch(actual, description)

            BaseFunctionalTest.logger.warn(description.toString())
            if (!failedAsserts.get()) {
                failedAsserts.set([])
            }
            failedAsserts.get().add new FailedAssert(description: description)
            false
        } else {
            if (!silence) {
                description.appendText("Assertion passed${assertionDescription ? ': ' + assertionDescription : ''}; Expected: ")
                        .appendDescriptionOf(matcher)
                logPassedAssertion(description.toString())
            }
            true
        }
    }

    public static Collection<FailedAssert> getStepFailedAssertions(BaseFunctionalTest.StepInfo step) {
        Collection<FailedAssert> found = [];
        if (failedAsserts.get()) {
            found = failedAsserts.get().findAll() { FailedAssert ass ->
                step.isChildOrSelf(ass.getStepInfo())
            }
        }
        found
    }



    public static boolean isStepFailed(BaseFunctionalTest.StepInfo step) {
        getStepFailedAssertions(step).asBoolean()
    }

    private static boolean weakAssert(String message, Boolean condition, Boolean isSilent) {
        def res = true
        if (!condition) {
            res = false
            BaseFunctionalTest.logger.warn("Assertion failed: ${message}")
            if (!failedAsserts.get()) {
                failedAsserts.set([])
            }
            Description description = new StringDescription();
            description.appendText("Asertion failed: ${message}")

            failedAsserts.get().add new FailedAssert(description: description)
        } else {
            if (!isSilent) logPassedAssertion("Assertion passed: ${message}")
        }
        res
    }

    public static boolean weakAssertTrue(String message, Boolean condition) {
        weakAssert(message, condition, false)
    }

    public static boolean weakAssertSilently(String message, Boolean condition) {
        weakAssert(message, condition, true)
    }


    public static boolean weakAssertSilentlyThat(String message, Object actual, Matcher matcher) {
        _weakAssertThat(message, actual, matcher, true)
    }

    public static boolean weakAssertThat(String message, Object actual, Matcher matcher) {
        _weakAssertThat(message, actual, matcher, false)
    }

    /**
     * To be used only for testing TESTS, not in SUT tests!!!
     *
     */
    public static void clearFailedWeakAsserts(){
        failedAsserts?.get()?.clear()
    }



    public static onTestFinish() {
        List<FailedAssert> assertionsFailed = failedAsserts.get()
        failedAsserts.set(null)
        if (assertionsFailed.asBoolean()) {
            throw new AssertionError("Some weak asserts failed ${assertionsFailed}")
        }
    }

    private static boolean _assertThat(String message, Object actual, Matcher matcher, boolean silence) {
        Description description = new StringDescription()
        if (!matcher.matches(actual)) {
            description.appendText(message)
                    .appendText("\nExpected: ")
                    .appendDescriptionOf(matcher)
                    .appendText("\n     but: ");
            matcher.describeMismatch(actual, description);
            BaseFunctionalTest.logger.error("Assertion failed. ${description.toString()}")
            throw new AssertionError(description.toString())
        } else {
            if (!silence) {
                description.appendText("$message; Expected: ")
                        .appendDescriptionOf(matcher)
                        .appendText(". Actual:${actual}")
                logPassedAssertion(description.toString())
            }
            true
        }
    }

    public static boolean assertTrue(String message, Boolean condition) {
        if (!condition) {
            assert condition, message
        }
        logPassedAssertion "Assertion passed: ${message}"
        true
    }

    public static boolean assertSilentlyThat(String message, Object actual, Matcher matcher) {
        _assertThat(message, actual, matcher, true)
    }

    public static boolean assertThat(String message, Object actual, Matcher matcher) {
        _assertThat(message, actual, matcher, false)
    }

}
